<?php
/**
 * Nweb Framework
 *
 * This script is protected by copyright. It's use, copying, modification
 * and distribution without written consent of the author is prohibited.
 *
 * @category    source
 * @package     pl.nweb.framework.application.bootstrap
 * @author      Krzysztof Kardasz <krzysztof.kardasz@gmail.com>
 * @copyright   Copyright (c) 2011 Krzysztof Kardasz
 * @license     http://www.gnu.org/licenses/lgpl-3.0.txt  GNU Lesser General Public
 * @version     3.0 $Id: bootstrap.php 465 2012-01-13 12:21:09Z krzysztof.kardasz $
 * @link        http://code.google.com/p/nweb-framework/
 * @link        http://framework.nweb.pl
 */

namespace pl\nweb\framework\application;

/**
 * Bootstrap
 *
 * @category    source
 * @package     pl.nweb.framework.application.bootstrap
 * @author      Krzysztof Kardasz <krzysztof.kardasz@gmail.com>
 * @copyright   Copyright (c) 2011 Krzysztof Kardasz
 * @version     3.0 $Id: bootstrap.php 465 2012-01-13 12:21:09Z krzysztof.kardasz $
 */
class Bootstrap
{
    /**
     * Parametry wywołania
     *
     * @var array
     */
    private $_options = array();

    /**
     * Obiekt autoloadera
     *
     * @var null|Autoloader
     */
    private $_autoloader = null;

    /**
     * Obiekt dyspozytora
     *
     * @var null|Dispatcher
     */
    private $_dispatcher = null;

    /**
     * Obiekt żądania
     *
     * @var null|Request
     */
    private $_request = null;

    /**
     * Obiekt odpowiedzi
     *
     * @var null|Response
     */
    private $_response = null;

    /**
     * Obiekt menadżera konfiguracji
     *
     * @var \pl\nweb\framework\config\manager\Application
     */
    private $_config = null;

    /**
     * Ścieżka do aplikacji
     *
     * @var string
     */
    private $_appPath = null;

    /**
     * Przestrzeń nazw aplikacji
     *
     * @var string
     */
    private $_appNs = null;

    /**
     * Opcje
     */
    const OPTION_APPLICATION_PATH      = 'application.path';
    const OPTION_APPLICATION_NAMESPACE = 'application.namespace';
    const OPTION_THROW_EXCEPTIONS      = 'throw.exceptions';
    const OPTION_AUTOLOADER            = 'autoloader.obj';
    const OPTION_CONFIG_MANAGER        = 'config.manager.obj';

    /**
     * Konstruktor
     *
     * @param  array $options opcje
     * @return void
     */
    final public function __construct (array $options = array())
    {
        if(isset($options[self::OPTION_APPLICATION_PATH])) {
            $this->_appPath = $options[self::OPTION_APPLICATION_PATH];
        }

        if(isset($options[self::OPTION_APPLICATION_NAMESPACE])) {
            $this->_appNs = $options[self::OPTION_APPLICATION_NAMESPACE];
        }

        if(isset($options[self::OPTION_AUTOLOADER])) {
            $this->_autoloader = $options[self::OPTION_AUTOLOADER];
        }

        if(isset($options[self::OPTION_CONFIG_MANAGER])) {
            $this->_config = $options[self::OPTION_CONFIG_MANAGER];
        }

        $this->_options = $options;

        // Konfiguracja zasobów
        $this->_setup();

        // Inicjacja
        $this->init();
    }

    /**
     * Definiowanie opcji
     *
     * @param  string $name nazwa
     * @param  mixed $value wartość
     * @return void
     */
    protected function _setOption ($name, &$value)
    {
        $this->_options[$name] = &$value;
    }

    /**
     * Zwraca flagę czy opcja została zdefiniowana
     *
     * @param  string $name nazwa
     * @return boolean
     */
    public function hasOption ($name)
    {
        return isset($this->_options[$name]);
    }

    /**
     *  Zwraca wartość ustawionej opcji
     *
     *  @param  string $name nazwa
     *  @param  mixed $default domyślna wartość
     *  @return mixed
     */
    public function & getOption ($name = null, $default = null)
    {
        if(null === $name) {
            return $this->_options;
        }

        if(isset($this->_options[$name])) {
            return $this->_options[$name];
        }
        return $default;
    }

    /**
     * Zwraca ścieżkę aplikacji
     *
     * @return string
     * @throws \pl\nweb\framework\application\bootstrap\Exception
     */
    public function getAppPath ()
    {
        if($this->_appPath === null) {
            throw new bootstrap\Exception ('Application path is not specified');
        }
        return $this->_appPath;
    }

    /**
     * Zwraca przestrzeń nazw aplikacji
     *
     * @return string
     * @throws \pl\nweb\framework\application\bootstrap\Exception
     */
    public function getAppNamespace ()
    {
        if($this->_appNs === null) {
            throw new bootstrap\Exception ('Application namespace is not specified');
        }
        return $this->_appNs;
    }

    /**
     * Zwraca obiekt autoloader
     *
     * @return \pl\nweb\framework\autoloader\Application
     * @throws \pl\nweb\framework\application\bootstrap\Exception
     */
    public function getAutoloader ()
    {
        if($this->_autoloader === null) {
            throw new bootstrap\Exception ('Autoloader object is not specified');
        }
        return $this->_autoloader;
    }

    /**
     * Definiowanie obiektu żądania
     *
     * @param \pl\nweb\framework\Request $request obiekt żądania
     * @return void
     */
    protected function _setRequest (\pl\nweb\framework\Request $request)
    {
        $this->_request = $request;
    }

    /**
     * Zwraca obiekt żądania
     *
     * @return \pl\nweb\framework\Request
     * @throws \pl\nweb\framework\application\bootstrap\Exception
     */
    public function getRequest ()
    {
        if($this->_request === null) {
            throw new bootstrap\Exception ('Request object is not specified');
        }
        return $this->_request;
    }

    /**
     * Definiowanie obiektu odpowiedzi
     *
     * @param \pl\nweb\framework\Response $request obiekt odpowiedzi
     * @return void
     */
    protected function _setResponse (\pl\nweb\framework\Response $response)
    {
        $this->_response = $response;
    }

    /**
     * Zwraca obiekt odpowiedzi
     *
     * @return \pl\nweb\framework\Response
     * @throws \pl\nweb\framework\application\bootstrap\Exception
     */
    public function getResponse ()
    {
        if($this->_response === null) {
            throw new bootstrap\Exception ('Response object is not specified');
        }
        return $this->_response;
    }

    /**
     * Definiowanie obiektu dispatchera
     *
     * @param dispatcher\iDispatcher $dispatcher obiekt dispatchera
     * @return void
     */
    protected function _setDispatcher (dispatcher\iDispatcher $dispatcher)
    {
        $this->_dispatcher = $dispatcher;
    }

    /**
     * Zwraca obiekt dispatchera
     *
     * @return \pl\nweb\framework\application\dispatcher\iDispatcher
     * @throws \pl\nweb\framework\application\bootstrap\Exception
     */
    protected function _getDispatcher ()
    {
        if($this->_dispatcher === null) {
            throw new bootstrap\Exception ('Dispatcher object is not specified');
        }
        return $this->_dispatcher;
    }

    /**
     * Zwraca obiekt menadżera konfiguracji (w przypadku braku podania argumentów) lub konfigurację (jeśli podano argumenty)
     *
     * @param string $name nazwa
     * @param string $format format pliku
     * @return \pl\nweb\framework\config\manager\Application
     * @throws \pl\nweb\framework\application\bootstrap\Exception
     */
    public function getConfig ()
    {
        if($this->_config === null) {
            throw new bootstrap\Exception ('Config object not defined');
        }
        $args = func_get_args();
        if(!empty($args)) {
            return call_user_func_array (array($this->_config, 'getConfig'), $args);
        }
        return $this->_config;
    }

    /**
     *  Inicjacja aplikacji
     *
     *  @return void
     */
    public function init()
    {}

    /**
     * Konfiguracja zasobów
     *
     * @return void
     */
    protected function _setup ()
    {
        $dispatcherResource = new \pl\nweb\framework\application\resource\Dispatcher($this->getConfig('application')->get('dispatcher'));
        $requestResource    = new \pl\nweb\framework\application\resource\Request($this->getConfig('application')->get('request'));
        $responseResource   = new \pl\nweb\framework\application\resource\Response($this->getConfig('application')->get('response'));

        $dispatcherResource->setOption(array(
            \pl\nweb\framework\application\Dispatcher::OPTION_APPLICATION_NAMESPACE => $this->getAppNamespace()
        ));
        $this->_dispatcher = $dispatcherResource->getObject();
        $this->_request    = $requestResource->getObject();
        $this->_response   = $responseResource->getObject();

        // TODO: ROUTER
    }

    /**
     *  Uruchomienie aplikacji
     *
     *  @return void
     */
    public function boot ()
    {
        // TODO: Event Manager
        // TODO: Router
        while(!$this->getRequest()->isDispatched()) {
            $this->_getDispatcher()->dispatch($this->getRequest(), $this->getResponse());
        }
        $this->getResponse()->send();
    }
}